package com.dding.cms.base.web;

import static com.dding.cms.base.web.util.PersistenceUtil.deleteCascadeBoExcept;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.dding.core.annotation.Service;
import org.dding.core.log.ILogger;
import org.dding.core.log.LoggerFactory;
import org.dding.core.util.StringUtils;
import org.dding.mvc.ActionSupport;
import org.dding.mvc.InvocationContext;
import org.dding.mvc.annotation.ActionMethod;
import org.dding.mvc.annotation.ActionMethod.ForwardMethod;
import org.javalite.activejdbc.Association;
import org.javalite.activejdbc.LazyList;
import org.javalite.activejdbc.Model;
import org.javalite.activejdbc.associations.Many2ManyAssociation;

import com.dding.cms.base.dao.ChannelDao;
import com.dding.cms.base.dao.impl.ChannelDaoImpl;
import com.dding.cms.base.entity.BaseModel;
import com.dding.cms.base.entity.BaseModelItem;
import com.dding.cms.base.entity.Channel;
import com.dding.cms.base.entity.ChannelAttr;
import com.dding.cms.base.entity.Site;
import com.dding.cms.base.web.bo.BaseModelBO;
import com.dding.cms.base.web.bo.BaseModelItemBO;
import com.dding.cms.base.web.bo.ChannelBO;
import com.dding.cms.base.web.util.ChannelDeleteCallbackListener;
import com.dding.cms.base.web.util.Configuration;
import com.dding.cms.base.web.util.Utils;

@Service(name = "channelAction")
public class ChannelAction extends ActionSupport<Object> {

	@SuppressWarnings("rawtypes")
	private static Set channelColums;

	public ChannelDao channelDao = new ChannelDaoImpl();

	private ILogger logger = LoggerFactory.getLogger(getClass());

	public ChannelAction() {
		Channel.addCallbacks(new ChannelDeleteCallbackListener());
	}

	@ActionMethod(
			name = "list", successView = "list.jsp", errorView = "error.html",
			httpMethod = ActionMethod.HttpMethod.ALL)
	@SuppressWarnings("unchecked")
	public void list(InvocationContext invocationContext) {
		LazyList<Model> list = Site
				.findBySQL("select site_id,site_name from jc_site");
		Map<Object, Object> sites = new HashMap<Object, Object>();
		for (Model model : list.toArray(new Model[0])) {
			sites.put(model.get("site_id"), model.get("site_name"));
		}
		invocationContext.getPageContext().setAttribute("sites", sites);

		Map<String, String> models = new HashMap<String, String>();
		list = BaseModel
				.findBySQL("select model_id,model_name,has_content,is_disabled from jc_model");
		for (Model model : list.toArray(new Model[0])) {
			Object object = model.get("is_disabled");
			if (object != null) {
				if ((Boolean) object) {
					continue;
				}
				BaseModelBO channelModel = (BaseModelBO) Utils.buildBO(model,
						BaseModelBO.class);
				models.put(channelModel.getModel_id().toString(),
						channelModel.getModel_name());
			}
		}
		invocationContext.getPageContext().setAttribute("models", models);

		String root = invocationContext.getParameterValue("root");
		String subQuery = null;
		if (StringUtils.isEmpty(root)) {
			subQuery = "parent_id is null";
		} else {
			subQuery = "parent_id = " + root;
		}

		List<ChannelBO> channels = new ArrayList<ChannelBO>();
		list = Channel.find(subQuery);
		for (Model model : list.toArray(new Model[0])) {
			ChannelBO channelBo = (ChannelBO) Utils.buildBO(model,
					ChannelBO.class);
			channels.add(channelBo);
		}
		invocationContext.getPageContext().setAttribute("channels", channels);
	}

	@ActionMethod(
			name = "beforeAdd", successView = "add.jsp",
			errorView = "error.html", httpMethod = ActionMethod.HttpMethod.GET)
	@SuppressWarnings("unchecked")
	public void beforeAdd(InvocationContext invocationContext) {
		String _site_id = invocationContext.getParameterValue("site_id");
		String _model_id = invocationContext.getParameterValue("model_id");
		String _model_type = invocationContext.getParameterValue("model_type");
		if (StringUtils.isEmpty(_model_id))
			return;

		LazyList<Model> channelModels = BaseModel
				.findBySQL(
						"select model_id,model_name,has_content from jc_model where model_id = ?",
						_model_id);
		if (channelModels == null || channelModels.size() == 0)
			return;
		Model channelModel = channelModels.get(0);
		if (channelModel == null)
			return;
		LazyList<BaseModelItem> items = channelModel.get(BaseModelItem.class,
				"model_type=?", _model_type);
		items.orderBy("priority asc");
		Boolean _has_content = (Boolean) channelModel.get("has_content");
		String _model_name = (String) channelModel.get("model_name");

		List<BaseModelItemBO[]> list = new ArrayList<BaseModelItemBO[]>();
		Model[] models = items.toArray(new Model[0]);
		BaseModelItemBO first = null, second = null;
		for (int i = 0; i < models.length; i++) {
			BaseModelItemBO item = buildItemBo(models[i]);
			if (item == null)
				continue;
			if (item.getIs_single()) {
				BaseModelItemBO[] bo = { item };
				list.add(bo);
				continue;
			} else {
				if (first == null)
					first = item;
				else
					second = item;
			}
			if (first != null && second != null) {
				BaseModelItemBO[] bo = { first, second };
				list.add(bo);
				first = null;
				second = null;
			}
		}
		if (second == null && first != null) {
			BaseModelItemBO[] bo1 = { first };
			list.add(bo1);
			first = null;
		}
		invocationContext.getPageContext().setAttribute("list", list);
		invocationContext.getPageContext().setAttribute("site_id", _site_id);
		invocationContext.getPageContext().setAttribute("model_id", _model_id);
		invocationContext.getPageContext().setAttribute("model_name",
				_model_name);
		invocationContext.getPageContext().setAttribute("has_content",
				_has_content ? "1" : "0");
	}

	@ActionMethod(
			name = "tree", errorView = "error.html",
			httpMethod = ActionMethod.HttpMethod.GET,
			forwardMethod = ForwardMethod.FLUSH)
	public void tree(InvocationContext invocationContext) {
		String tree = null;
		try {
			tree = channelDao.getChannelTree();
		} catch (Throwable e) {
			logger.warn(e);
		}
		ajaxJsonMessage(tree, invocationContext);
	}

	@ActionMethod(
			name = "add", successView = "list.do", errorView = "error.html",
			httpMethod = ActionMethod.HttpMethod.ALL)
	public void add(InvocationContext invocationContext) {

		Map<String, String[]> channelMap = invocationContext.getParameterMap();
		List<ChannelAttr> list = new ArrayList<ChannelAttr>();
		Channel channel = new Channel();
		initColumns();
		for (Entry<String, String[]> entry : channelMap.entrySet()) {
			if (channelColums.contains(entry.getKey())) {
				channel.set(entry.getKey(), StringUtils.isEmpty(entry
						.getValue()[0]) ? null : entry.getValue()[0]);
			} else {
				ChannelAttr attr = new ChannelAttr();
				attr.set("attr_name", entry.getKey());
				attr.set("attr_value", entry.getValue()[0]);
				list.add(attr);
			}
		}
		// TODO:批量添加
		channel.save();
		for (ChannelAttr attr : list) {
			channel.add(attr);
		}
	}

	@SuppressWarnings("unchecked")
	@ActionMethod(
			name = "delete", errorView = "error.html",
			forwardMethod = ForwardMethod.FLUSH)
	public void delete(InvocationContext invocationContext) {
		List<Association> associations = Channel.associations();
		List<Association> params = new ArrayList<Association>();
		for (Association association : associations) {
			if (association instanceof Many2ManyAssociation)
				params.add(association);
		}
		deleteCascadeBoExcept(invocationContext, new Channel(), "channel_id",
				Association[].class, params.toArray(new Association[0]));
	}

	@SuppressWarnings("unchecked")
	@ActionMethod(
			name = "update", successView = "list.do", errorView = "error.html",
			httpMethod = ActionMethod.HttpMethod.POST)
	public void update(InvocationContext invocationContext) {
		initColumns();

		Map<String, String[]> modelMap = invocationContext.getParameterMap();
		String[] id = modelMap.get("channel_id");
		if (id[0] == null)
			return;
		StringBuilder updates = new StringBuilder();
		List<String> params = new ArrayList<String>();
		List<Object[]> params1 = new ArrayList<Object[]>();
		for (Entry<String, String[]> entry : modelMap.entrySet()) {
			if ("channel_id".equals(entry.getKey()))
				continue;
			if (!channelColums.contains(entry.getKey())) {
				Object[] array = new Object[3];
				array[0] = entry.getValue()[0];
				array[1] = entry.getKey();
				array[2] = id[0];
				params1.add(array);
			} else {
				updates.append(entry.getKey() + "=?");
				updates.append(",");
				params.add(StringUtils.isEmpty(entry.getValue()[0]) ? null
						: entry.getValue()[0]);
			}
		}
		params.add(id[0]);

		Channel.update(updates.substring(0, updates.length() - 1),
				"channel_id=?", params.toArray());

		String query = "attr_value=?";
		String conditions = "attr_name=? and channel_id=?";
		ChannelAttr
				.update(query, conditions, params1.toArray(new Object[0][0]));
	}

	@ActionMethod(
			name = "edit", successView = "edit.jsp", errorView = "error.html",
			httpMethod = ActionMethod.HttpMethod.GET)
	public void edit(InvocationContext invocationContext) {
		String id = invocationContext.getParameterValue("id");
		if (!StringUtils.isEmpty(id)) {
			Model model = Channel.findById(id);
			Map<String, Object> attrs = (Map<String, Object>) Utils.buildBoMap(
					model, ChannelBO.class);
			beforeAdd(invocationContext);
			LazyList<ChannelAttr> attrList = model.getAll(ChannelAttr.class);
			for (ChannelAttr attr : attrList) {
				attrs.put((String) attr.get("attr_name"),
						(String) attr.get("attr_value"));
			}
			invocationContext.getPageContext().setAttribute("attrs", attrs);
		}
	}

	private void initColumns() {
		if (channelColums == null)
			channelColums = Channel.getMetaModel().getColumnMetadata().keySet();
	}

	private BaseModelItemBO buildItemBo(Model model) {
		BaseModelItemBO item = (BaseModelItemBO) Utils.buildBO(model,
				BaseModelItemBO.class);
		if (!item.getIs_display())
			return null;
		item.setDataTypeMapping(Configuration.getDataTypeMapping().get(
				item.getData_type()));
		return item;
	}
}